#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/epoll.h>


#define BUF_SIZE 100
#define EPOLL_SIZE 50



void error_handling(char *buf);
void* thread_main(void *arg);


struct thread_param
{
	int fd;
	char buf[BUF_SIZE];
	int strLen;
};


int main(int argc, char *argv[]){
	
	int serv_sock,clnt_sock;
	struct sockaddr_in serv_adr,clnt_adr;
	socklen_t adr_sz;
	int str_len,i;
	char buf[BUF_SIZE];
	pthread_t t_id;
	void *thr_ret;
	struct thread_param threadParam;

	struct epoll_event *ep_events;
	struct epoll_event event;
	int epfd, event_cnt;

	if (argc!=2)
	{
		printf("usage error\n");
		exit(1);
	}
	serv_sock = socket(PF_INET, SOCK_STREAM ,0);
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family=AF_INET;
	serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_adr.sin_port=htons(atoi(argv[1]));
	
	if (bind(serv_sock, (struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)
	{
		error_handling("bind error");
	}

	if (listen(serv_sock,5)==-1)
	{
		error_handling("listen error");
	}


	epfd = epoll_create(EPOLL_SIZE);
	ep_events = malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
	event.events = EPOLLIN;
	event.data.fd = serv_sock;
	epoll_ctl(epfd, EPOLL_CTL_ADD,serv_sock, &event);

	while (1)
	{
		event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);
		if (event_cnt ==-1)
		{
			puts("epoll wait error\n");
			break;
		}
		for(i =0; i<event_cnt; i++)
		{
			if(ep_events[i].data.fd == serv_sock)
			{
				adr_sz = sizeof(clnt_adr);
				clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr,&adr_sz);
				event.events = EPOLLIN;
				event.data.fd = clnt_sock;
				epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock,&event);
				printf("connnected client： %d\n", clnt_sock);

			}
			else
			{
				str_len = read(ep_events[i].data.fd,buf,BUF_SIZE);
				if (str_len ==0)
				{
					epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd,NULL);
					close(ep_events[i].data.fd);
					printf("closed client: %d \n", ep_events[i].data.fd);
				}
				else
				{
					// write(ep_events[i].data.fd, buf, str_len);
					threadParam.fd = ep_events[i].data.fd;
					strcpy(threadParam.buf, buf);
					threadParam.strLen = str_len;

					if(pthread_create(&t_id, NULL, thread_main, (void *)&threadParam))
					{					
						puts("ptread_create() error\n");
						return -1;
					}
				}
			}
		}
	}
	close(serv_sock);
	close(epfd);
	return 0;



}

void* thread_main(void *arg){
	struct thread_param param = *((struct thread_param*)arg);
	pthread_t pid = pthread_self();
	printf("thread id:%d --> begin process...\n",pid);
	if(param.fd % 2 ==0)
		sleep(10);
	else
		sleep(1);

	write(param.fd, param.buf, param.strLen);
	printf("thread id:%d --> end process...\n",pid);
	return NULL;
}


void error_handling(char* buf){
	fputs(buf, stderr);
	fputs('\n', stderr);
	exit(1);
}